﻿using Games.Common;
using Games.Data;
using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace Games.Horse64
{
    [DesignTimeVisible(false)]
    partial class Horse64Page : ContentPage
    {
        IDataBase database = DependencyService.Get<IDataBase>();
        double cellSize;
        GameCell lastCell;
        private bool isOver = true;
        double layoutWidth, layoutHeight;
        Game game = new Game();
        GameInfo gameInfo;
        GameCell current;
        double xMargin, yMargin;

        internal Horse64Page(GameInfo gamePage)
        {
            InitializeComponent();
            BindingContext = gameInfo = gamePage;
            database.CreateTable<HorseRecord>();
        }
        void OnLayoutSizeChanged(object sender, EventArgs args)
        {
            Layout layout = sender as Layout;
            layoutWidth = layout.Width * 50 / 51;
            layoutHeight = layout.Height * 50 / 51;
            var sizew = layoutWidth / Game.LENGTH;
            var sizeh = layoutHeight / Game.LENGTH;
            cellSize = Math.Min(sizew, sizeh);
            var laySize = Game.LENGTH * cellSize;
            absoluteLayout.WidthRequest = absoluteLayout.HeightRequest = laySize;

            xMargin = (layoutWidth - laySize) / 2.0 + layout.Width / 51 / 3;
            yMargin = (layoutHeight - laySize) / 2.0 + layout.Width / 51 / 3;
            InitLayout();
        }
        void InitLayout()
        {
            if(isOver)
            {
                game.Init();
            }
            else
            {
                game.Reload();
            }
            isOver = false;
            gameInfo.StepCount = 1;
            var count = Game.LENGTH * Game.LENGTH;

            if(absoluteLayout.Children.Count == 0)
            {
                while(absoluteLayout.Children.Count < count)
                {
                    GameCell lifeCell = new GameCell();
                    lifeCell.Tapped += OnTapGestureTapped;
                    absoluteLayout.Children.Add(lifeCell);
                }
                current = new GameCell()
                {
                    BackImage = GameCell.ImgPath
                };
                absoluteLayout.Children.Add(current);
            }
            int index = 0;

            for(int x = 0; x < Game.LENGTH; x++)
            {
                for(int y = 0; y < Game.LENGTH; y++)
                {
                    GameCell lifeCell = (GameCell)absoluteLayout.Children[index];
                    lifeCell.Col = x;
                    lifeCell.Row = y;
                    Rectangle rect = new Rectangle(x * cellSize + xMargin, y * cellSize + yMargin, cellSize, cellSize);
                    AbsoluteLayout.SetLayoutBounds(lifeCell, rect);
                    if(x == game.CurrentX && y == game.CurrentY)
                    {
                        lifeCell.Status = CellStatus.Current;
                        lifeCell.Text = gameInfo.StepCount.ToString();
                        lastCell = lifeCell;
                        AbsoluteLayout.SetLayoutBounds(current, rect);
                        current.Show(0, 3);
                    }
                    else
                    {
                        lifeCell.Status = CellStatus.Init;
                        lifeCell.Text = null;
                    }
                    index++;
                }
            }
            current.Text = gameInfo.StepCount.ToString();
            TimeCounter.Start();
        }
        async void OnTapGestureTapped(object sender, EventArgs args)
        {
            if(isOver)
            {
                return;
            }
            GameCell lifeCell = (GameCell)sender;
            if(lifeCell == current)
            {
                return;
            }
            if(game.Visit(lifeCell.Col, lifeCell.Row))
            {
                if(lastCell != null)
                {
                    lastCell.Status = CellStatus.Visited;
                }
                var toBound = AbsoluteLayout.GetLayoutBounds(lifeCell);
                await Move(toBound.X, toBound.Y);
                lifeCell.Status = CellStatus.Current;
                lastCell = lifeCell;
                gameInfo.StepCount++;
                lifeCell.Text = gameInfo.StepCount.ToString();
                current.Text = gameInfo.StepCount.ToString();
                if(gameInfo.StepCount == Game.LENGTH * Game.LENGTH || !game.CanMove())
                {
                    await ShowFinish();
                }
            }
        }

        async void TapGestureRecognizer_Tapped(object sender, EventArgs e)
        {
            if(gameInfo.StepCount <= 0)
            {
                return;
            }
            if(isOver)
            {
                isOver = false;
            }
            if(game.Back(out int x, out int y))
            {
                var lifeCell = lastCell;
                lastCell = absoluteLayout.Children.OfType<GameCell>().FirstOrDefault(c => c.Col == x && c.Row == y);

                lifeCell.Status = CellStatus.Init;
                lastCell.Status = CellStatus.Current;
                var toBound = AbsoluteLayout.GetLayoutBounds(lastCell);
                await Move(toBound.X, toBound.Y);
                gameInfo.StepCount--;
                current.Text = gameInfo.StepCount.ToString();
            }
        }
        async Task Move(double tox, double toy)
        {
            var fromBound = AbsoluteLayout.GetLayoutBounds(current);
            var fromx = fromBound.X;
            var fromy = fromBound.Y;
            var taskCompletionSource = new TaskCompletionSource<bool>();
            current.Animate<Rectangle>(
                "Push",
                t => new Rectangle(fromx + t * (tox - fromx), fromy + t * (toy - fromy), fromBound.Width, fromBound.Height),
                r => AbsoluteLayout.SetLayoutBounds(current, r),
                16,
                100,
                Easing.Linear,
                (r, b) => taskCompletionSource.SetResult(b)
            );
            await current.ScaleBack(1.6, 100);
        }
        async Task ShowFinish()
        {
            isOver = true;
            var record = await SaveRecordAsync();
            await DisplayAlert(record.Level, $"{record.MessageString}", "OK");
        }
        async Task<Record> SaveRecordAsync()
        {
            var dur = TimeCounter.Stop();
            var cRecord = new HorseRecord()
            {
                GameID = gameInfo.ID,
                Step = gameInfo.StepCount,
                CreateTime = DateTime.Now,
                StartX = game.StartX,
                StartY = game.StartY,
                EndX = game.CurrentX,
                EndY = game.CurrentY,
                Duration = dur
            };
            await database.SaveRecordByDiff(cRecord, 100,
                o => true,
                list => list.OrderByDescending(x => x.Step).ThenBy(x => x.Duration).ThenByDescending(x => x.ID),
                o => o.Step < cRecord.Step || (o.Step == cRecord.Step && o.Duration > cRecord.Duration)
            );
            return cRecord.Convert();
        }

        void RestartClicked(object sender, EventArgs args)
        {
            InitLayout();
        }

        private void TapToBack(object sender, EventArgs e)
        {
            Navigation.PopAsync();
        }

        void OnAboutButtonClicked(object sender, EventArgs args)
        {
            if(!mask.Contains("about"))
            {
                var aboutView = ControlUtil.GetAbout(gameInfo);
                mask.Add("about", gameInfo.Name, aboutView);
            }
            mask.ShowWithCancel("about", "确定");
        }
        void OnRecordClicked(object sender, EventArgs args)
        {
            //var sorter = new Sorter<HorseRecord>(q => q.OrderByDescending(x => x.Step).ThenBy(x => x.Duration).ThenByDescending(x => x.ID));
            Navigation.PushAsync(new RecordPage(gameInfo.Name, new RecordViewModel<HorseRecord>(null)));
        }
    }
}